#define	_ARH_KONTEKST_C_
#include "kontekst.h"
#include "spremnik.h"
#include <jezgra/ispis.h>

//opis konteksta dretve u koju se vraca: gdje je kontekst
uint16 arh_opisnik_konteksta = 0;	//opisnik segmenta gdje je stog
uint32 arh_kontekst_aktivne = 0;	//adresa vrha stoga s kontekstom

//prije povratka u dretvu
void arh_azuriraj_kontekst (void *_kontekst)
{
	arh_kontekst_t *kontekst = _kontekst;
	
	arh_opisnik_konteksta = kontekst->ss;
	arh_kontekst_aktivne = kontekst->esp;
	//j_ispisi("--ss=%x, esp=%x\n", kontekst->ss, kontekst->esp);
}

//pri prekidu, prije obrade
void arh_pohrani_adresu_konteksta (void *_kontekst, void *vrh_stoga)
{
	arh_kontekst_t *kontekst = _kontekst;

	kontekst->esp = (uint32) vrh_stoga;
}

//dohvati adresu vrha stoga dretve - tu su parametri za jezgrinu funkciju
//na dretvinu stog (ispod prekidnog konteksta dretve)
void *arh_dohvati_vrh_stoga_dretve ( void *_spremnik, void *_kontekst )
{
	arh_spremnik_t *spremnik = _spremnik;
	kontekst_dretve_t *kontekst = _kontekst;

	if ( spremnik->razina == RAZINA_KORISNIKA )
		return (void *) ( kontekst->esp );
	else
		return ( (void *) kontekst ) + 
			( sizeof (kontekst_dretve_t) - 2 * sizeof(uint32) );
}

// stog dretve mora biti spreman za povratak iz prekida
void arh_izgradi_pocetni_stog ( void *_spremnik, void *_kontekst,
			         void *stog, void *pokreni_dretvu,
			         void *poc_funkc, void *param )
{
	arh_spremnik_t *spremnik = _spremnik;
	arh_kontekst_t *kontekst = _kontekst;
	void *astog;

	kontekst_dretve_t *pstog;

	//stavi parametre na dretvin stog
	if ( spremnik->odmak != 0 ) { //nije jezgrina dretva koja vidi sve
		astog = arh_puj ( spremnik, stog );
	}
	else {
		astog = stog;
	}

	STAVI_NA_STOG ( astog, param );
	STAVI_NA_STOG ( astog, poc_funkc );
	STAVI_NA_STOG ( astog, 0 );

	if ( spremnik->razina == RAZINA_KORISNIKA ) {
		pstog = &kontekst->kontekst; //kontekst se sprema u opisnik dr.

		pstog->eflags = POC_REG_STANJA;	// registar stanja

		pstog->eip = (uint32) pokreni_dretvu;	// programsko br.

		//dretvin stog (koji koristi van jezgre)
		pstog->ss = spremnik->op_podataka;
		pstog->esp = (uint32) arh_jup ( spremnik, astog );

		//opisnik stoga za obnovu (pohranu) konteksta - u jezgri
		kontekst->ss = GDT_OPISNIK ( SEGJ_PODACI, GDT, RAZINA_JEZGRE );
		kontekst->esp = (uint32) pstog;
	}
	else {	//jezgrina dretva
		//"malo drukcije" - koristi se samo jedan stog za sve!
		pstog =  astog - 
			( sizeof (kontekst_dretve_t) - 2 * sizeof (uint32) );

		pstog->eflags = POC_REG_STANJA_JD; // registar stanja - u jezgri

		if ( spremnik->odmak == 0 ) {
			//"umetnuta" funkcija za stvaranje dretvi je u relat.
			//adresnom prostoru, a ova dretva je u apsolutnom
			extern char programi_pocetak;
			pstog->eip = ( (uint32) pokreni_dretvu ) +
				     ( (uint32) &programi_pocetak );
		}
		else {
			pstog->eip = (uint32) pokreni_dretvu; //programsko br.
		}

		//ne dirati pstog->ss i pstog->esp - ne koriste se!

		//opisnik stoga za obnovu/pohranu konteksta - dretvin stog!
		kontekst->ss = spremnik->op_podataka;
		kontekst->esp = (uint32) arh_jup ( spremnik, pstog );
	}

	pstog->cs = spremnik->op_koda;
	pstog->ds = spremnik->op_podataka;
	pstog->es = spremnik->op_podataka;
	pstog->fs = spremnik->op_podataka;
	pstog->gs = spremnik->op_podataka;

	pstog->kod_greske = 0;	// zamjenski kod greske

	// sadrzaj opcih registara je nebitan (pstog->registri[])

	//arh_ispisi_kontekst(pstog);
}

void *arh_dodaj_obradu ( void *spr, void *_kontekst, void *obrada,
			 uint32 vp_na_stogu, ... )
//"vp_na_stogu" = velicina parametara (...) na stogu u 'uint32' jedinicama
//umetnuta funkcija mora izgledati: um_fun(void *stari_vrh_stoga, ...)
//ali ne koristiti 'stari_vrh_stoga' jer to nije adresa u procesu (nije rel.)
{
	arh_spremnik_t *spremnik = spr;
	arh_kontekst_t *kontekst = _kontekst;
	kontekst_dretve_t *kontekst_dr, *kopija;
	uint32 *param = &vp_na_stogu;
	uint32 *stog;
	void *stog_prije;// = arh_puj(NULL, (void *) kontekst->esp);
	int i;

	if ( spremnik->razina == RAZINA_KORISNIKA ) {
		kontekst_dr = &kontekst->kontekst;

		stog_prije = arh_puj (spremnik, (void *) kontekst_dr->esp );

		//kopirati kontekst na stog dretve
		kopija = stog_prije;
		kopija--; //mjesto za kopiju konteksta
		*kopija = *kontekst_dr; //kopira kontekst na stog dretve

		//umetnuti umetnutu funkciju u programsko brojilo
		kontekst_dr->eip = (uint32) obrada;

		stog = (uint32 *) kopija;
		stog -= vp_na_stogu + 2;
		//+1 za povratnu adresu (nula);
		//+1 za adresu vrha stoga prije = prvi parametar umetnute fun.

		kontekst_dr->esp = (uint32) arh_jup ( spremnik, stog );

		*stog = 0;//"povratna adresa" na vrhu stoga

		*( ++stog ) = (uint32) stog_prije; //prvi parametar funkcije

		//stavi (prekopiraj) parametre na stog dretve
		for ( i = 0; i < vp_na_stogu; i++ ) {
			*( ++stog ) = *( ++param );
		}

	}
	else {	//jezgrina dretva
		stog_prije = kontekst_dr = arh_puj ( spremnik, 
						     (void *) kontekst->esp );

		stog = stog_prije;
		stog -= vp_na_stogu + 2;
		//+1 za povratnu adresu (nula);
		//+1 za adresu vrha stoga prije = prvi parametar umetnute fun.

		kopija = (void *) stog - 
			( sizeof (kontekst_dretve_t) - 2 * sizeof (uint32) );
		*kopija = *kontekst_dr;

		if ( spremnik->odmak == 0 ) {
			extern char programi_pocetak;
			kopija->eip = ( (uint32) obrada ) +
				      ( (uint32) &programi_pocetak );
		}
		else {
			kopija->eip = (uint32) obrada;
		}

		*stog = 0;//"povratna adresa" na vrhu stoga

		*( ++stog ) = (uint32) stog_prije; //prvi parametar funkcije

		//stavi (prekopiraj) parametre na stog dretve
		for ( i = 0; i < vp_na_stogu; i++ ) {
			*( ++stog ) = *( ++param );
		}

		kontekst->esp = (uint32) arh_jup ( spremnik, kopija );
	}

	return stog_prije;
}

void arh_makni_obradu (void *_kontekst, void *vrh_stoga)
{
	arh_kontekst_t *kontekst = _kontekst;
	kontekst_dretve_t *kontekst_dr = &kontekst->kontekst, *kopija;

	if (kontekst->spremnik->razina == RAZINA_KORISNIKA) {
		kopija = vrh_stoga;
		kopija--; //stari kontekst je na vrhu stoga (iznad 'vrh_stoga')
	
		//provjeri je li to u adr. prostoru procesa!
		//(za napraviti)

		*kontekst_dr = *kopija; //obnavlja kontekst sa stoga dretve
		//time ce se azurirati i kazaljka stoga
	}
	else {
		kontekst->esp = (uint32) arh_jup ( kontekst->spremnik,
						   vrh_stoga );
	}
}

void arh_ispisi_kontekst ( void *_kontekst )
{
	kontekst_dretve_t *k = _kontekst;

	j_ispisi ( "segm:\t%x %x %x %x\n", (uint32) k->gs, (uint32) k->fs,
		   (uint32) k->es, (uint32) k->ds );

	j_ispisi ( "kodgr:\t%d\n", k->kod_greske );

	j_ispisi ( "eip:\t%x\n", k->eip );

	j_ispisi ( "cs:\t%x\n", k->cs );

	j_ispisi ( "eflags:\t%x\n", k->eflags );

	j_ispisi ( "esp:\t%x\n", k->esp );

	j_ispisi ( "ss:\t%x\n", k->ss );
}

/*

http://www.angelcode.com/dev/callconv/callconv.html
http://en.wikipedia.org/wiki/X86_calling_conventions

*/